﻿using System;
using System.IO;
using System.Threading;
using SerialManager.Loaders;
using Lang = SerialManager.Localization.SerialManager;

namespace SerialManager
{
    /// <summary>
    /// Exit code used by <see cref="Core.Exit"/> method
    /// It's numeric valus is returned by Main method
    /// </summary>
    public enum ExitCode
    {
        UnknownError = -1,
        Success = 0,
        DirectoryNotFound = 1,
        DirectoryIsEmpty = 2,
        NoValidFiles = 10,
        NothingToDo = 11,
        MissingArguments = 50,
        WrongArguments = 51,
    }

    /// <summary>
    /// Main class of program
    /// </summary>
    internal class Core
    {
        private readonly static log4net.ILog Logger
            = log4net.LogManager.GetLogger(typeof(Core));

        /// <summary>
        /// Indicates, that application is terminating
        /// It's set by <see cref="Core.Exit"/> method
        /// </summary>
        public static bool IsTerminating { get; private set; }

        /// <summary>
        /// Title of currently processed serial (from command line)
        /// It's same name as subdirectory name relatively to working directory
        /// </summary>
        public static string SerialTitle { get; private set; }

        /// <summary>
        /// Path to currently processed serial directory
        /// </summary>
        public static DirectoryInfo SerialDirectory { get; private set; }

        /// <summary>
        /// Main entry point
        /// </summary>
        /// <param name="args"></param>
        internal static int Main(string[] args)
        {
            #region Basic initialization
            // no arguments suplied, display help and exit
            if (args.Length == 0)
            {
                WriteHelp();
                return Exit(ExitCode.MissingArguments);
            }

            AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(UnhandledException);
            Thread.CurrentThread.Name = "Main";

            var param = args[0];
            if (File.Exists(param))
            {
                Logger.Info("Given parameter cannot be existing file");
                WriteHelp();
                return Exit(ExitCode.WrongArguments);
            }

            if (Directory.Exists(param))
            {
                Logger.DebugFormat("Given existing directory {0} as parameter", param);
                SerialDirectory = new DirectoryInfo(param);
                SerialTitle = SerialDirectory.Name;
            }
            else
            {
                Logger.DebugFormat("Given existing directory {0} as parameter", param);
                SerialTitle = args[0];
                SerialDirectory = new DirectoryInfo(Path.Combine(Environment.CurrentDirectory, SerialTitle));
            }
            #endregion

            #region Main loop that runs only once, for purpose of exiting program at some point
            while (true)
            {
                var builder = new FileListBuilder(SerialDirectory);
                if (builder.Status != FileListBuilderStatus.Initialized)
                    break;

                #region Reading serial information

                var loader = EpisodeLoader.Create<TvRageLoader>(SerialTitle, true);

                // wait for loader to finish or until user press abort key
                while (!loader.Finished)
                {
                    // build file list in first loop
                    if (builder.Status != FileListBuilderStatus.BuildingFinished)
                        builder.BuildFilesList();
                    // builder didn't found any valid files, information about serial is not needed
                    else if (builder.FoundFiles.Count == 0)
                    {
                        loader.Abort(EpisodeLoaderAbortReason.NotNeeded);
                        Logger.Info(Lang.NoValidFilesFound);
                        Exit(ExitCode.NoValidFiles);
                        break;
                    }

                    // user can abort by pressing key
                    if (Console.KeyAvailable)
                    {
                        var key = Console.ReadKey(true);
                        if (key.Key == Config.AbortKey)
                        {
                            loader.Abort(EpisodeLoaderAbortReason.UserAborted);
                            break;
                        }
                    }
                }

                #endregion

                if (IsTerminating)
                    goto Exit;

                switch (loader.Status)
                {
                    case EpisodeLoaderStatus.DoneSuccessful:
                        Logger.InfoFormat("Found information about {0} episodes in {1} seasons", loader.Episodes.Count, loader.SeasonCount);
                        break;
                    case EpisodeLoaderStatus.DoneWithError:
                        Logger.Info("Error occured while retrieving informations.");
                        goto default;
                    case EpisodeLoaderStatus.Aborted:
                    default:
                        Logger.Info(Lang.EpisodeInformationNotUsed);
                        break;
                }

                Console.WriteLine();

                builder.PrepareRename(loader.Episodes);
                if (builder.Status != FileListBuilderStatus.MovementPrepared)
                    break;

                DoRenameWork(builder);

                break;
            }
            #endregion

        Exit:
            return Environment.ExitCode;

            /*
            if(  unknown.Count > 0 )
            {
                var unknownPath = Path.Combine( SerialDirectory.FullName, "!unknown" );
                Console.WriteLine();
                Console.WriteLine( "Bylo nalezeno {0} neznámých souborů. Chceš je přesunout do adresáře {1} ?", unknown.Count, unknownPath );
                Console.WriteLine( "[A]no ouhlasím, [N]echci to, [D]etailní informace" );
                Console.TreatControlCAsInput = true;
                var key = Console.ReadKey( true );
                unknown.ForEach(
                    f =>
                    {
                        if( !Directory.Exists( unknownPath ) )
                            Directory.CreateDirectory( unknownPath );
                    }
                );
            }

            if( noMatch.Count > 0 )
            {
                WriteError( "Následující soubory mají neznámý formát názvu:" );
                foreach( var noMatchItem in noMatch )
                    Console.WriteLine( noMatchItem );
            }*/
        }

        private static void DoRenameWork(FileListBuilder builder)
        {
            if (builder.FilesForMove.Count == 0)
            {
                Logger.Info("All files has right names, nothing to be renamed.");
                Exit(ExitCode.NothingToDo);
                Console.ReadKey();
                return;
            }

            Logger.InfoFormat("Will rename {0} files. This operation cannot be reverted.", builder.FilesForMove.Count);
            Console.WriteLine("[Y]es do it, [N]o way !, [D]etail information");

            if (Logger.IsDebugEnabled)
            {
                foreach (var moveInfo in builder.FilesForMove)
                    Logger.DebugFormat("{0} => {1}", moveInfo.Key.Name, moveInfo.Value.FullName);
            }

            while (true)
            {
                Console.TreatControlCAsInput = true;
                var key = Console.ReadKey(true);

                if (key.Key == ConsoleKey.D)
                {
                    Console.WriteLine();
                    foreach (var moveInfo in builder.FilesForMove)
                    {
                        Console.WriteLine(" <= " + moveInfo.Key.FullName);
                        Console.WriteLine(" => " + moveInfo.Value);
                        Console.WriteLine();
                    }
                    Console.WriteLine("So what now, can i do it ? Please...");
                    Console.WriteLine("[Y]es do it, [N]o way !");
                    continue;
                }

                if (key.Key == ConsoleKey.N)
                {
                    Console.WriteLine("Allright, i don't touch that.");
                    return;
                }
                else if (key.Key == ConsoleKey.Y)
                {
                    Console.WriteLine("You had been warned, processing...");
                    builder.ProcessRename();
                    Console.WriteLine("All files has been successfully renamed.");
                    return;
                }
            }
        }

        /// <summary>
        /// Handler pro zpracovani neosetrenych vyjimek
        /// </summary>
        static void UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            if (Logger.IsDebugEnabled)
            {
                Logger.Debug("Caught an unhandled exception.");
                Logger.Debug(e.ExceptionObject);
            }

            if (e.IsTerminating)
                Exit();
        }

        /// <summary>
        /// Ukoncit aplikaci s neznamym chybovym kodem
        /// </summary>
        public static int Exit()
        {
            return Exit(ExitCode.UnknownError);
        }

        /// <summary>
        /// Ukoncit aplikaci se zadanym kodem
        /// </summary>
        /// <param name="code"></param>
        public static int Exit(ExitCode code)
        {
            Logger.DebugFormat("Application was terminated with code {0} ({1})", (int)code, code);

            IsTerminating = true;
            Environment.ExitCode = (int)code;

            Console.WriteLine();
            Console.WriteLine("Press any key to exit");

            Console.TreatControlCAsInput = true;
            while (!Console.KeyAvailable)
                continue;

            return (int)code;
        }

        /// <summary>
        /// Vypsat napovedu pokud nejsou specikovany potrebne parametery
        /// </summary>
        private static void WriteHelp()
        {
            Console.WriteLine("Supply name of subdirectory with serial as parameter");
        }

        /// <summary>
        /// Fixes filename for Windows system
        /// NULL is converted to empty string
        /// Removes forbidden chars
        /// </summary>
        public static string FixFileName(string filename)
        {
            if (filename == null)
                return "";
            var sb = new System.Text.StringBuilder(filename);
            var forbiddenChars = new string[] { "*", "\"", "/", "\\", "[", "]", ":", ";", "|", "=", ",", "?" };
            foreach (var c in forbiddenChars)
                sb.Replace(c, "");
            return sb.ToString();
        }
    }
}
